<?php
/**
 * @package Curly
 * @subpackage Event
 * @version 0.9
 * @link http://curly.codeplex.com/
 * @license http://curly.codeplex.com/license The MIT License
 * @author Dawid Zawada
 */

namespace Curly\Event
{
    /**
     * Base class for events
     * 
     * It allows you to attach and detach objects implementing
     * the SplObserver interface and callback functions, that have
     * one parameter of SplSubject type.
     * Event can be fired by invoking the notify method
     * 
     * @package Curly
 	 * @subpackage Event
     * @license http://curly.codeplex.com/license The MIT License
     */
    class Event implements \SplSubject
    {
        /**
         * Collection of objects implementing the SplObserver interface
         * 
         * @var array
         */
        protected $observerObjects = array();
        
        /**
         * Collection of callback functions
         * 
         * @var array
         */
        protected $observerFunctions = array();
        
        /**
         * Attaches the given SplObserver so that it can be 
         * notified when the event is fired
         * 
         * @param \SplObserver $observer
         */
        public function attach( \SplObserver $observer )
        {
            static::addObserver( $this->observerObjects, $observer );
        }
        
        /**
         * Attaches the given callback function so that it can be 
         * invoked when the event is fired
         * 
         * @param \Closure $callback
         */
        public function attachFunc( \Closure $callback )
        {
            static::addObserver( $this->observerFunctions, $callback );
        }
        
        /**
         * Detaches the given SplObserver from the event
         * 
         * @param \SplObserver $observer
         */
        public function detach( \SplObserver $observer )
        {
            static::removeObserver( $this->observerObjects, $observer );
        }
        
        /**
         * Detaches the given callback function from the event
         * 
         * @param \Closure $callback
         */
        public function detachFunc( \Closure $callback )
        {
            static::removeObserver( $this->observerFunctions, $callback );
        }
        
        /**
         * Notifies all attached observers and invokes all attached
         * callback functions
         */
        public function notify()
        {
            foreach( $this->observerObjects as $observer )
            {
                $observer->update( $this );
            }
            foreach( $this->observerFunctions as $callback )
            {
                $callback( $this );
            }
        }

        /**
         * Adds the given observer (either a SplObserver object 
         * or a callback function) to the given container
         * 
         * @param array $container
         * @param mixed $observer
         */
        protected static function addObserver( array &$container, $observer )
        {
            if( array_search( $observer, $container ) === false )
            {
                $container[] = $observer;
            }
        }
        
        /**
         * Removes the given observer (either a SplObserver object
         * or a callback function) from the given container
         * 
         * @param array $container
         * @param mixed $observer
         */
        protected static function removeObserver( array &$container, $observer )
        {
            if( ( $key = array_search( $observer, $container ) ) !== false )
            {
                unset( $container[$key] );
            }
        }
    }
}